import { EventEmitter } from "@/framework/util/event-emitter.ts";

/**
 * WebSocket客户端基类
 */
export class BaseWebSocket<T extends Dict = {}> extends EventEmitter<T> {
  protected _ws: WebSocket | null = null;
  private _tmpWs: WebSocket | null = null;
  protected _closed = false;

  public constructor(public url: string) {
    super();
  }

  get isConnected() {
    return this._ws?.readyState === WebSocket.OPEN;
  }

  /**
   * 连接到服务端
   */
  connect() {
    if (this._ws) return;
    this._closed = false;
    const ws = new WebSocket(this.url);
    ws.onopen = this._onOpen;
    ws.onclose = this._onClose;
    ws.onerror = this._onError;
    ws.onmessage = this._onMessage;
    this._ws = ws;
  }

  /**
   * 断开连接
   */
  disconnect() {
    this._closed = true;
    this._release();
  }

  private _release() {
    if (this._ws != null) {
      this._ws.close();
      this._ws.onopen = null;
      // this._ws.onclose = null;
      this._ws.onerror = null;
      this._ws.onmessage = null;

      this._tmpWs = this._ws;
      this._ws = null;
    }
  }

  private _onOpen = (e: Event) => {
    if (__DEV__) console.log("[websocket] opened", this.url, e);
    this.onOpen?.(e);
  };

  private _onClose = (e: CloseEvent) => {
    console.log("[websocket] closed", this.url, e);

    this._release();
    this.onClose?.(e);

    if (this._closed) {
      if (this._tmpWs != null) {
        this._tmpWs.onclose = null;
        this._tmpWs = null;
      }
      return;
    }
    // 重连
    setTimeout(() => {
      if (!this._closed) {
        this.connect();
      }
    }, 3000);
  };

  private _onError = (e: Event) => {
    console.log("[websocket] error", this.url, e);
    this.onError?.(e);
  };

  private _onMessage = (e: MessageEvent) => {
    this.onMessage?.(e);
  };

  /**
   * 连接成功
   * @param e
   * @protected
   */
  protected onOpen?(e: Event): void;

  /**
   * 连接关闭
   * @param e
   * @protected
   */
  protected onClose?(e: CloseEvent): void;

  /**
   * 连接错误
   * @param e
   * @protected
   */
  protected onError?(e: Event): void;

  /**
   * 收到消息
   * @param e
   * @protected
   */
  protected onMessage?(e: MessageEvent): void;
}
